package pool;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author zy.du
 * @date 2020/11/11 2:20 PM
 */
public class ConnectionManager {

    private InetSocketAddress inetSocketAddress;

    /**
     * 总创建链接数
     */
    private AtomicInteger totalCount = new AtomicInteger();

    /**
     * 空闲链接数
     */
    private AtomicInteger freeCount = new AtomicInteger();

    /**
     * 公平锁
     */
    private ReentrantLock lock = new ReentrantLock(true);

    private Condition condition = lock.newCondition();

    /**
     * 空闲链接
     */
    private LinkedList<Connection> freeConnections = new LinkedList<>();

    private ConnectionManager() { }

    public ConnectionManager(InetSocketAddress socketAddress) {
        this.inetSocketAddress = socketAddress;
    }

    public Connection getConnection() {

        lock.lock(); // 必须放在外部

        try {

            Connection connection;
            while (true) {
                // 检查剩余空闲链接数量
                if (freeCount.get() > 0) {
                    //空闲数减1
                    freeCount.decrementAndGet();
                    //取一个connection
                    connection = freeConnections.poll();
                    //最大idle空闲时间1h，超过会释放socket
                    if (!connection.isAvaliable() ||
                            (System.currentTimeMillis() - connection.getLastAccessTime()) > 3600) {
                        closeConnection(connection);
                        continue;
                    }

                    //connection是否进行tcp测试, 只测试一次
                    if (connection.isNeedActiveTest()) {
                        boolean isActive;
                        try {
                            isActive = connection.activeTest();
                        } catch (IOException e) {
                            System.err.println("send to server[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] active test error ,emsg:" + e.getMessage());
                            isActive = false;
                        }
                        if (!isActive) {
                            //测试失败则关闭连接, 继续获取
                            closeConnection(connection);
                            continue;
                        } else {
                            connection.setNeedActiveTest(false);
                        }
                    }
                } else if (500 == 0 || totalCount.get() < 500) {
                    // 当前pool池里连接小于500，则创建
                    connection = ConnectionFactory.create(this.inetSocketAddress);
                    totalCount.incrementAndGet();
                } else {
                    try {
                        //max_wait_time_in_ms 自旋等待1s
                        if (condition.await(1000, TimeUnit.MILLISECONDS)) {
                            //wait single success
                            continue;
                        }
                        throw new RuntimeException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, wait_time > 1000ms");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        throw new RuntimeException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, emsg:" + e.getMessage());
                    }
                }
                return connection;
            }

        } finally {
            lock.unlock();
        }
    }

    /**
     * 关闭连接
     * @param connection
     */
    public void closeConnection(Connection connection) {
        try {
            if (connection != null) {
                totalCount.decrementAndGet();
                //关闭socket
                connection.closeDirectly();
            }
        } catch (IOException e) {
            System.err.println("close socket[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] error ,emsg:" + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 释放连接(归还poll)
     * @param connection
     */
    public void releaseConnection(Connection connection) {
        if (connection == null) {
            return;
        }
        lock.lock();
        try {
            connection.setLastAccessTime(System.currentTimeMillis());
            freeConnections.add(connection);
            freeCount.incrementAndGet();
            //通知可用连接
            condition.signal();
        } finally {
            lock.unlock();
        }

    }

    /**
     * 设置所有空闲连接为必须测试active可用性
     */
    public void setActiveTestFlag() {
        if (freeCount.get() > 0) {
            lock.lock();
            try {
                for (Connection freeConnection : freeConnections) {
                    freeConnection.setNeedActiveTest(true);
                }
            } finally {
                lock.unlock();
            }
        }
    }

}
